<?php

declare(strict_types=1);

namespace App\Model\Service;

use App\Cache\RedisKey;
use App\Constants\InviteVideoCode;
use App\Helpers\RedisUtil;
use App\Constants\ErrorCode;
use App\Helpers\ConfigUtil;
use App\Model\CoinLog;
use App\Model\InviteVideoLog;
use App\Model\UserCoin;
use App\Model\UserProfile;
use App\Service\ServiceTrait\InviteVideoCheckTrait;
use Hyperf\DbConnection\Db;
use Psr\EventDispatcher\EventDispatcherInterface;
use App\Event\ConsumeEvent;
use App\Model\CallBase;
use App\Model\ServiceFeeLog;
use App\Model\User;
use App\Model\VideoCall;
use App\Model\VoiceCall;
use RedisException;
/**
 *  消费服务模型
 */
class ConsumeService
{
    use InviteVideoCheckTrait;
    /**
     * 结算通话消费
     * @param int $callType 1-视频通话，0-语音通话
     */
    static function settleCall($callRecord = null, int $callType = VoiceCall::CALL_TYPE)
    {
        if (!$callRecord || !$callRecord['call_time']) {
            log_info(__CLASS__ . "==CallType:{$callType}==NoRecord");
            return false;
        }
        if (!is_array($callRecord)) $callRecord = $callRecord->toArray();

        //消费方和收入方
        $userId = $callRecord['user_id'];
        $hostUserId = $callRecord['chat_user_id'];
        //小助手对话不处理
        if (User::checkOfficial([$userId, $hostUserId])) {
            log_info(__CLASS__ . "==CallType:{$callType}==OfficialRecord");
            return false;
        }

        $consumeLockKey = RedisKey::operatePaymentLock($callRecord['id'], $callType);
        if (!(RedisUtil::addLock($consumeLockKey, 10))) {
            return false;
        }

        //通话时间
        $mins = CallBase::getCallMinutes($callRecord['call_time']);

        //话单单价
        $coinSetting = CallBase::getCallCoinSetting($callRecord, $callType);
        $totalCost = $mins * $coinSetting;
        if (!$totalCost) {
            //清空冻结
            UserCoin::clearFreezeCoin($userId);
            log_info(__METHOD__ . "==CallType:{$callType}==NoCost==Time:{$mins}==CoinSet:{$coinSetting}==Log:{$callRecord['id']}");
            return false;
        }
        //个人虚拟币账户信息
        $userCoin = UserCoin::getUserInfo($userId);
        //余额不足
        if ($userCoin['real_total_coin'] < $totalCost) {
            $realCostCoin = $userCoin['real_total_coin'];
            log_info(__METHOD__ . "==LackCoin==CallType:{$callType}==UserCoin:{$userCoin['real_total_coin']}==TotalCost:{$totalCost}==Log:{$callRecord['id']}");
            if ($realCostCoin <= 0) return false;
        }
        //余额足够
        else $realCostCoin = $totalCost;

        //更新账户信息
        self::doCoinUpdateForCall(
            $userId,
            $hostUserId,
            $realCostCoin,
            $userCoin,
            $callRecord
        );
        log_info(__METHOD__ . "==DoneConsume==CallType:{$callType}==UserCoin:{$userCoin['real_total_coin']}==TotalCost:{$totalCost}==Log:{$callRecord['id']}");

        return true;
    }

    /**
     * 结算免费福利通话
     */
    static function settleCallForFree($callRecord = null, $callType = VoiceCall::CALL_TYPE)
    {
        if (!$callRecord || !$callRecord['call_time']) return false;
        if (!is_array($callRecord)) $callRecord = $callRecord->toArray();
        //消费方
        $userId = $callRecord['user_id'];
        $hostUserId = $callRecord['chat_user_id'];
        //通话时间
        $mins = CallBase::getCallMinutes($callRecord['call_time']);

        //用户花费（新首福利免费）
        $realCost = 0;
        //通话设置的收费（语音和视频）
        $coinSetting = CallBase::getCallCoinSetting($callRecord, $callType);
        $totalCost = $mins * $coinSetting;
        //该单免费
        if ($coinSetting <= 0 || !$totalCost) {
            log_info(__METHOD__ . "==NoCost==Time:{$mins}==CoinSet:{$coinSetting}==Log:{$callRecord['id']}");
            return false;
        }

        //生成订单号
        $orderId = self::genOrderId($callRecord['id'], $callType == VoiceCall::CALL_TYPE ? 'voice' : 'video');

        //话单类
        $callCls = $callType == VoiceCall::CALL_TYPE ? VoiceCall::class : VideoCall::class;

        //更新账户和流水
        Db::beginTransaction();
        try {
            //平台抽成服务费
            $serviceFee = self::proccessServiceFee($totalCost, $orderId, $hostUserId);

            //扣除服务提成
            $realInCome = $totalCost - $serviceFee;

            //更新主播账户
            $hostRemainCoin = UserCoin::gainIncome($hostUserId, $realInCome);

            //添加流水记录
            $userCoin = UserCoin::getUserInfo($userId);
            //流水来源
            $coinLogFrom = CoinLog::FROM_DAILY_FREE;
            CoinLog::addConsume($userId, $realCost, $coinLogFrom, $userCoin, $orderId, $hostUserId);
            CoinLog::addIncome($hostUserId, $realInCome, $coinLogFrom, $hostRemainCoin, $orderId, $userId);

            //更新话单表
            $callCls::updateInfo($callRecord['id'], [
                'order_id' => $orderId,
                'total_cost' => $totalCost,
                'caller_cost' => $realCost,
                'host_gain' => $realInCome,
                'allowance' => $realInCome
            ]);

            //减少剩余免费语聊时长
            UserProfile::delFreeCallTime($userId, $mins);

            Db::commit();
        } catch (\Throwable $exception) {
            log_info(__METHOD__ . '==Exception:' . $exception->getMessage());
            Db::rollBack();
        }

        //魅力值，豪气值变更事件
        make(EventDispatcherInterface::class)->dispatch(new ConsumeEvent(
            $userId,
            $hostUserId,
            $realInCome,
            $realInCome,
            false,
            CoinLog::FROM_INVITE_INCOME_CALL
        ));

        return true;
    }

    //邀约视频消费
    public function settleCallForInvite($callRecord = null, $callType = VoiceCall::CALL_TYPE){
        if (!$callRecord || !$callRecord['call_time']) return false;
        if (!is_array($callRecord)) $callRecord = $callRecord->toArray();

        //消费方
        $femaleUserId = $hostUserId = $callRecord['user_id'];
        $maleUserId = $userId = $callRecord['chat_user_id'];
        //通话时间
        $mins = CallBase::getCallMinutes($callRecord['call_time']);

        //用户花费（新首福利免费）
        $realCost = 0;
        $totalCost = 0;
        //通话设置的收费
        if (time() - $callRecord['answer_time'] > (InviteVideoCode::ANSWER_TIME_PAY * 60)){
            $totalCost = CallBase::getCallCoinSetting($callRecord, $callType);
        }

        //该单免费
//        if ($coinSetting <= 0 || !$totalCost) {
//            log_info(__METHOD__ . "==NoCost==Time:{$mins}==CoinSet:{$coinSetting}==Log:{$callRecord['id']}");
//            return false;
//        }

        $consumeLockKey = RedisKey::operatePaymentLock($callRecord['id'], $callType);
        if (!(RedisUtil::addLock($consumeLockKey, 10))) {
            return false;
        }

        //生成订单号
        $orderId = self::genOrderId($callRecord['id'], $callType == VoiceCall::CALL_TYPE ? 'voice' : 'video');

        //话单类
        $callCls = $callType == VoiceCall::CALL_TYPE ? VoiceCall::class : VideoCall::class;

        //更新账户和流水
        Db::beginTransaction();
        try {
            //更新主播账户
            $hostRemainCoin = UserCoin::gainIncome($hostUserId, $totalCost);

            //添加流水记录
            $userCoin = UserCoin::getUserInfo($userId);
            //流水来源
            $coinLogFrom = CoinLog::FROM_INVITE_VIDEO;
            CoinLog::addConsume($userId, $realCost, $coinLogFrom, $userCoin, $orderId, $hostUserId);
            CoinLog::addIncome($hostUserId, $totalCost, $coinLogFrom, $hostRemainCoin, $orderId, $userId);

            //更新话单表
            $callCls::updateInfo($callRecord['id'], [
                'order_id' => $orderId,
                'total_cost' => $totalCost,
                'caller_cost' => $realCost,
                'host_gain' => $totalCost,
                'allowance' => $totalCost
            ]);

            $this->reloadData($maleUserId,$femaleUserId,InviteVideoLog::STATUS_FINISH);

            Db::commit();
        } catch (\Throwable $exception) {
            log_info(__METHOD__ . '==Exception:' . $exception->getMessage());
            Db::rollBack();
        }

        return true;
    }

    /**
     * 特惠视频消费
     * @param $callRecord
     * @param int $callType
     * @return bool
     */
    static function settleCallByDiscount($callRecord = null, int $callType = VoiceCall::CALL_TYPE)
    {
        if (!$callRecord || !$callRecord['call_time']) {
            log_info(__CLASS__ . "==DiscountVideo:CallType:{$callType}==NoRecord");

            return false;
        }

        if (!is_array($callRecord)) {
            $callRecord = $callRecord->toArray();
        }

        //消费方和收入方
        $userId = $callRecord['user_id'];
        $hostUserId = $callRecord['chat_user_id'];

        //小助手对话不处理
        if (User::checkOfficial([$userId, $hostUserId])) {
            log_info(__CLASS__ . "==DiscountVideo::userId:{$userId}==CallType:{$callType}==OfficialRecord");

            return false;
        }

        $consumeLockKey = RedisKey::operatePaymentLock($callRecord['id'], $callType);
        if (!(RedisUtil::addLock($consumeLockKey, 10))) {
            return false;
        }

        //通话时间
        $mins = CallBase::getCallMinutes($callRecord['call_time']);

        //话单单价
        $coinSetting = CallBase::getCallCoinSetting($callRecord, $callType);
        $totalCost = $mins * $coinSetting;
        if (!$totalCost) {
            //清空冻结
            UserCoin::clearFreezeCoin($userId);

            log_info(__METHOD__ . "==DiscountVideo::userId:{$userId}==CallType:{$callType}==NoCost==Time:{$mins}==CoinSet:{$coinSetting}==Log:{$callRecord['id']}");

            return false;
        }

        //个人虚拟币账户信息
        $userCoin = UserCoin::getUserInfo($userId);

        //余额不足
        if ($userCoin['real_total_coin'] < $totalCost) {
            $realCostCoin = $userCoin['real_total_coin'];
            log_info(__METHOD__ . "==DiscountVideo::userId:{$userId}==LackCoin==CallType:{$callType}==UserCoin:{$userCoin['real_total_coin']}==TotalCost:{$totalCost}==Log:{$callRecord['id']}");
            if ($realCostCoin <= 0) return false;
        }
        //余额足够
        else $realCostCoin = $totalCost;

        //更新账户信息
        self::doCoinUpdateForCall(
            $userId,
            $hostUserId,
            $realCostCoin,
            $userCoin,
            $callRecord
        );

        log_info(__METHOD__ . "==DiscountVideo::userId:{$userId}==DoneConsume==CallType:{$callType}==UserCoin:{$userCoin['real_total_coin']}==TotalCost:{$totalCost}==Log:{$callRecord['id']}");

        return true;
    }

    /**
     * 更新双方账户收入
     */
    static function doCoinUpdateForCall(
        $userId = 0,
        $hostUserId = 0,
        $realCostCoin = 0,
        $userCoin = [],
        $callLog = []
    ) {
        $callLogId = $callLog['id'];
        $callType = $callLog['call_type'];

        //查询用户的虚拟账户
        if (!$userCoin) $userCoin = UserCoin::getUserInfo($userId);

        //生成订单号
        $orderId = self::genOrderId($callLogId, $callType == VoiceCall::CALL_TYPE ? 'voice' : 'video');
        $callClass = $callType == VoiceCall::CALL_TYPE ? VoiceCall::class : VideoCall::class;
        $logFromOut = CoinLog::getCallFrom($callType, $callLog['from'], 'out');
        $logFromIn = CoinLog::getCallFrom($callType, $callLog['from'], 'in');

        //更新账户和流水
        Db::beginTransaction();
        try {
            //更新账户
            $remainCoin = UserCoin::costCoin($userId, $realCostCoin, $userCoin);
            //清空冻结账户
            UserCoin::clearFreezeCoin($userId);

            //添加流水记录
            CoinLog::addConsume(
                $userId,
                $realCostCoin,
                $logFromOut,
                $remainCoin,
                $orderId,
                $hostUserId
            );

            //平台抽成服务费
            $serviceFee = self::proccessServiceFee($realCostCoin, $orderId, $hostUserId);

            //主播实际收入
            $realInCome = $realCostCoin - $serviceFee;
            //更新主播账户
            $remainCoin = UserCoin::gainIncome($hostUserId, $realInCome);
            //添加流水记录
            CoinLog::addIncome(
                $hostUserId,
                $realInCome,
                $logFromIn,
                $remainCoin,
                $orderId,
                $userId
            );

            //更新语聊表
            $callClass::updateInfo($callLogId, [
                'order_id' => $orderId,
                'total_cost' => $realCostCoin,
                'caller_cost' => $realCostCoin,
                'host_gain' => $realInCome,
                'allowance' => 0
            ]);

            Db::commit();
        } catch (\Throwable $exception) {
            log_info(__METHOD__ . '==Exception:' . $exception->getMessage());
            Db::rollBack();
        }

        //魅力值，豪气值变更事件
        make(EventDispatcherInterface::class)->dispatch(new ConsumeEvent(
            $userId,
            $hostUserId,
            $realCostCoin,
            $realInCome,
            true,
            CoinLog::FROM_INVITE_INCOME_CALL
        ));
    }

    /**
     * 生成消费订单号
     */
    static function genOrderId($consumeLogId = 0, $type = 'voice')
    {
        return date('YmdHis') . "_{$type}_{$consumeLogId}";
    }

    /**
     * 送礼消费
     */
    static function sendGift(
        $sendGiftRecord = null,
        $userCoin = [],
        $receiverGain = true,
        $senderCost = true
    ) {
        if (!$sendGiftRecord) return false;
        //订单号
        $orderId = self::genOrderId($sendGiftRecord['id'], 'gift');
        return self::commonProccess(
            $sendGiftRecord['user_id'],
            $sendGiftRecord['receive_user_id'],
            $sendGiftRecord['gift_coin'],
            CoinLog::FROM_SEND_GIFT,
            CoinLog::FROM_GET_GIFT,
            CoinLog::FROM_INVITE_INCOME_GIFT,
            $orderId,
            $userCoin,
            $receiverGain,
            $senderCost
        );
    }

    /**
     * 私聊收费
     */
    static function sendPrivateMsg($userId = 0, $receiveUserId = 0, $chatCoin = 0)
    {
        return self::commonProccess(
            $userId,
            $receiveUserId,
            $chatCoin,
            CoinLog::FROM_SEND_PRIVATE_MSG_OUT,
            CoinLog::FROM_SEND_PRIVATE_MSG_IN,
            CoinLog::FROM_INVITE_PRIVATE_MSG
        );
    }

    /**
     * 打招呼消息收费
     */
    static function sendGreetMsg($userId = 0, $receiveUserIds = [], $msgCoin = 0)
    {
        if (empty($receiveUserIds)) return false;
        if (!is_array($receiveUserIds)) $receiveUserIds = [$receiveUserIds];
        $succUids = [];
        foreach ($receiveUserIds as $receiveUserId) {
            $result = self::commonProccess(
                $userId,
                $receiveUserId,
                $msgCoin,
                CoinLog::FROM_SEND_GREET_MSG_OUT,
                CoinLog::FROM_SEND_GREET_MSG_IN,
                CoinLog::FROM_INVITE_PRIVATE_MSG
            );
            $result && array_push($succUids, $receiveUserId);
        }
        return $succUids;
    }

    /**
     * 查看微信消费
     */
    static function checkWechat(
        $userId = 0,
        $receiveUserId = 0,
        $costCoin = 0,
        $userCoin = [],
        $receiverGain = true
    ) {
        return self::commonProccess(
            $userId,
            $receiveUserId,
            $costCoin,
            CoinLog::FROM_CHECK_WECHAT_OUT,
            CoinLog::FROM_CHECK_WECHAT_IN,
            0,
            '',
            $userCoin,
            $receiverGain
        );
    }

    /**
     * 许愿消费
     */
    static function makeWish($userId = 0, $costCoin = 0, $userCoin = [])
    {
        return self::commonProccess($userId, 0, $costCoin, CoinLog::FROM_ACT_MAKE_WISH, 0, 0, '', $userCoin, false);
    }

    /**
     * 通用处理
     */
    static function commonProccess(
        $userId = 0,
        $receiveUserId = 0,
        $costCoin = 0,
        $consumeFrom = CoinLog::FROM_SEND_PRIVATE_MSG_OUT,
        $incomeFrom = CoinLog::FROM_SEND_PRIVATE_MSG_IN,
        $inviteFrom = CoinLog::FROM_INVITE_PRIVATE_MSG,
        $orderId = '',
        $userCoin = [],
        $receiverGain = true,
        $senderCost = true
    ) {
        if (!$userId || $costCoin <= 0) return false;
        !$userCoin && $userCoin = UserCoin::getUserInfo($userId);
        //余额不足
        if ($userCoin['total_coin'] < $costCoin) return false;
        //生成订单号
        !$orderId && $orderId = self::genOrderId($userId . '_' . $receiveUserId, $consumeFrom);
        //更新账户和流水
        Db::beginTransaction();
        try {
            if ($senderCost) {
                //更新账户
                $remainCoin = UserCoin::costCoin($userId, $costCoin, $userCoin);
                //添加流水记录
                CoinLog::addConsume(
                    $userId,
                    $costCoin,
                    $consumeFrom,
                    $remainCoin,
                    $orderId,
                    $receiveUserId
                );
            }

            //主播收入增加
            $realInCome = 0;
            if ($receiverGain) {
                //平台抽成服务费记录
                $serviceFee = self::proccessServiceFee($costCoin, $orderId, $receiveUserId);
                //主播实际收入
                $realInCome = $costCoin - $serviceFee;
                //更新对方账户
                $remainCoin = UserCoin::gainIncome($receiveUserId, $realInCome);
                //添加流水记录
                CoinLog::addIncome(
                    $receiveUserId,
                    $realInCome,
                    $incomeFrom,
                    $remainCoin,
                    $orderId,
                    $userId
                );
            }

            Db::commit();
        } catch (\Throwable $exception) {
            log_info(__METHOD__ . "==Order:{$orderId}==Exception:" . $exception->getMessage());
            Db::rollBack();
            return false;
        }
        //魅力值，豪气值变更事件
        $inviteFrom > 0 && make(EventDispatcherInterface::class)->dispatch(new ConsumeEvent(
            $userId,
            $receiveUserId,
            $costCoin,
            $realInCome,
            true,
            $inviteFrom
        ));
        return true;
    }

    /**
     * 计算处理服务费提成
     */
    static function proccessServiceFee($totalCost = 0, $orderId = '', $hostUserId = 0)
    {
        $serviceFee = round($totalCost * ConfigUtil::getConsumeRate($hostUserId), 1);
        ServiceFeeLog::addConsume($orderId, $serviceFee);
        return $serviceFee;
    }

    /**
     * 增加收入
     */
    static function gainIncome($userId, $incomeCoin = 0, $from = 1, $opUserId = 0)
    {
        $userCoin = UserCoin::gainIncome($userId, $incomeCoin);
        $ret = CoinLog::addIncome($userId, $incomeCoin, $from, $userCoin, '', $opUserId);
        return $ret;
    }

    /**
     * 增加充值帐户收入
     */
    static function gainRechargeIncome($userId, $incomeCoin = 0, $from = 1, $opUserId = 0)
    {
        $userCoin = UserCoin::gainRecharge($userId, $incomeCoin);
        return CoinLog::addIncome($userId, $incomeCoin, $from, $userCoin, '', $opUserId);
    }
}
